/*
 * crc.h
 *
 *  Created on: 2023年6月29日
 *      Author: JX Fung
 */
/** @file
 * @brief CRC computation function
 */

#ifndef CRC_H
#define CRC_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>



#ifdef __cplusplus
extern "C" {
#endif

/* Initial value expected to be used at the beginning of the crc8_ccitt
 * computation.
 */
#define CRC8_CCITT_INITIAL_VALUE 0xFF

/**
 * @defgroup checksum Checksum
 */

/**
 * @defgroup crc CRC
 * @ingroup checksum
 * @{
 */


enum crc_type {
	CRC8,	     
	CRC8_CCITT, 
	CRC16,	   
	CRC16_ANSI, 
	CRC16_CCITT, 
	CRC16_ITU_T, 
	CRC32_C,     
	CRC32_IEEE, 
};

/**
 * @brief 不带输入输出反转
 *
 * @return The computed CRC16 value (不带XOR)
 */
uint16_t crc16(uint16_t poly, uint16_t seed, const uint8_t *src, size_t len);

/**
 * @brief 带输入输出反转
 *
 * - CRC-16/ANSI, CRC-16/MODBUS, CRC-16/USB, CRC-16/IBM
 *   https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-modbus
 *   poly: 0x8005 (0xA001) initial seed: 0xffff, xor output: 0x0000
 *
 * @param poly 
 * @param seed 
 * @param src 
 * @param len 
 *
 * @return The computed CRC16 value (不带XOR)
 */
uint16_t crc16_reflect(uint16_t poly, uint16_t seed, const uint8_t *src, size_t len);
/**
 * @brief CRC 8
 *	 参考https://reveng.sourceforge.io/crc-catalogue/1-15.htm#crc.cat-bits.8
 * @param src 
 * @param len 
 * @param polynomial 
 * @param initial_value 
 * @param reversed 
 * @return
 */
uint8_t crc8(const uint8_t *src, size_t len, uint8_t polynomial, uint8_t initial_value,
	  bool reversed);

/**
 * @brief polynomial = 0x1021, 反转输入输出
 *
 *
 * - CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-16/KERMIT
 *   参考https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-kermit
 *   initial seed: 0x0000, xor output: 0x0000
 *
 * - CRC-16/X-25, CRC-16/IBM-SDLC, CRC-16/ISO-HDLC
 *   参考https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-ibm-sdlc
 *   initial seed: 0xffff, xor output: 0xffff
 *
 *
 * @param seed 
 * @param src 
 * @param len 
 *
 * @return The computed CRC16 value (without any XOR applied to it)
 */
uint16_t crc16_ccitt(uint16_t seed, const uint8_t *src, size_t len);

/**
 * @brief polynomial 0x1021,  不带输入输出反转
 *
 *
 * - CRC-16/XMODEM, CRC-16/ACORN, CRC-16/LTE
 *   参考https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-xmodem
 *   initial seed: 0x0000, xor output: 0x0000
 *
 * - CRC16/CCITT-FALSE, CRC-16/IBM-3740, CRC-16/AUTOSAR
 *   参考https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-ibm-3740
 *   initial seed: 0xffff, xor output: 0x0000
 *
 * - CRC-16/GSM
 *   参考https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-gsm
 *   initial seed: 0x0000, xor output: 0xffff
 *
 *
 * @param seed 
 * @param src 
 * @param len 
 *
 * @return The computed CRC16 value (without any XOR applied to it)
 */
uint16_t crc16_itu_t(uint16_t seed, const uint8_t *src, size_t len);

/**
 * @brief Compute the ANSI (or Modbus) variant of CRC-16
 *
 * The ANSI variant of CRC-16 uses 0x8005 (0xA001 reflected) as its polynomial
 * with the initial * value set to 0xffff.
 *
 * @param src 
 * @param len
 *
 * @return 
 */
static inline uint16_t crc16_ansi(const uint8_t *src, size_t len)
{
	return crc16_reflect(0xA001, 0xffff, src, len);
}

/**
 * @brief 
 * @param  *data        
 * @param  len  
 *
 * @return CRC32 value.
 *
 */
uint32_t crc32_ieee(const uint8_t *data, size_t len);

/**
 * @brief 
 *
 * @param crc  
 * @param *data
 * @param len   
 *
 * @return CRC32 value.
 *
 */
uint32_t crc32_ieee_update(uint32_t crc, const uint8_t *data, size_t len);

/**
 * @brief CRC32
 *
 * @param crc       
 * @param *data     
 * @param len       
 * @param first_pkt 数据流第一包.
 * @param last_pkt  数据流最后一包.
 *
 * @return CRC32 value.
 *
 */
uint32_t crc32_c(uint32_t crc, const uint8_t *data,
		 size_t len, bool first_pkt, bool last_pkt);

/**
 * @brief
 *
 *        CCITT ploy= 0x07.
 *
 * @param initial_value 初始值
 * @param buf 
 * @param len 
 *
 * @return CRC8 value
 */
uint8_t crc8_ccitt(uint8_t initial_value, const void *buf, size_t len);


/**
 * @brief Compute a CRC checksum, in a generic way.
 *
 * @param type CRC算法
 * @param src 计算的源数据
 * @param len 源数据长度
 * @param seed 初始值
 * @param poly 多项式
 * @param reflect 反转
 * @param first 只用于crc32
 * @param last 只用于crc32
 * @return uint32_t CRC value
 */
static inline uint32_t crc_by_type(enum crc_type type, const uint8_t *src, size_t len,
				   uint32_t seed, uint32_t poly, bool reflect, bool first,
				   bool last)
{
	switch (type) {		
	case CRC8:
		return crc8(src, len, poly, seed, reflect);
			
	case CRC8_CCITT:
		return crc8_ccitt(seed, src, len);
	case CRC16:
		if (reflect) {
			return crc16_reflect(poly, seed, src, len);
		} else {
			return crc16(poly, seed, src, len);
		}
	case CRC16_ANSI:
		return crc16_ansi(src, len);
	case CRC16_CCITT:
		return crc16_ccitt(seed, src, len);
	case CRC16_ITU_T:
		return crc16_itu_t(seed, src, len);		
	case CRC32_C:
		return crc32_c(seed, src, len, first, last);
		
	case CRC32_IEEE:
		return crc32_ieee_update(seed, src, len);	
	default:
		break;
	}


	return -1;
}

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif
